package ldh.im.fxbase.component;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.StackPane;
import javafx.util.Callback;
import ldh.im.fxbase.cache.SoftId;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;

import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;

public class LimitDataListView<T> extends StackPane {

    private ObservableList<T> datas = FXCollections.observableArrayList();
    private Integer limitTotal = 200;
    private VirtualFlow<T, ?> messageList;
    private VirtualizedScrollPane virtualizedScrollPane = null;
    private double scrollY = 0L;
    private int posIndex = 0;
    private boolean isLoading = false;
    private Callback<LimitDataListView, List<T>> loadFrontCallback = null;
    private Callback<LimitDataListView, List<T>> loadLastCallback = null;

    public LimitDataListView(Function<T, Node> function) {
        this(function, null, null);
    }

    public LimitDataListView(Function<T, Node> function, Callback<LimitDataListView, List<T>> loadFrontCallback, Callback<LimitDataListView, List<T>> loadLastCallback ) {
        messageList = VirtualFlow.createVertical(datas, t->{
            return Cell.wrapNode(function.apply(t));
        });

        this.loadFrontCallback = loadFrontCallback;
        this.loadLastCallback = loadLastCallback;

        virtualizedScrollPane = new VirtualizedScrollPane<>(messageList);
        this.getChildren().add(virtualizedScrollPane);

        bindEvent();
    }

    public void setLoadFrontCallback(Callback<LimitDataListView, List<T>> loadFirstCallback) {
        this.loadFrontCallback = loadFirstCallback;
    }

    public void setLoadLastCallback(Callback<LimitDataListView, List<T>> loadLastCallback) {
        this.loadLastCallback = loadLastCallback;
    }

    public Integer getLimitTotal() {
        return limitTotal;
    }

    public void setLimitTotal(Integer limitTotal) {
        this.limitTotal = limitTotal;
    }

    public ObservableList<T> getItems() {
        return datas;
    }

    public VirtualizedScrollPane getVirtualizedScrollPane() {
        return virtualizedScrollPane;
    }

    public void addFront(T data) {
        datas.add(data);
        while (datas.size() > limitTotal) {
            datas.remove(datas.size()-1);
        }
        messageList.show(datas.size());
    }

    public void addFronts(List<T> datasList) {
        datas.addAll(0, datasList);
        if (datas.size() > limitTotal) {
            datas.remove(limitTotal, datas.size());
        }
//        messageList.show(datasList.size());
    }

    public void addLast(T data) {
        datas.addAll(data);
        if (datas.size() > limitTotal) {
            int t = datas.size() - limitTotal;
            datas.remove(0, t);
        }
        messageList.show(datas.size());
    }

    public void addLasts(List<T> datasList) {
        datas.addAll(datasList);
        if (datas.size() > limitTotal) {
            int t = datas.size() - limitTotal;
            datas.remove(0, t);

        }
//        int size = datas.size() - datasList.size();
//        messageList.show(size);
    }

    private void bindEvent() {
        this.addEventFilter(ScrollEvent.ANY, e-> {
            if (isLoading) return;
            if (posIndex == 0) {
                scrollY = this.virtualizedScrollPane.getEstimatedScrollY();
            }
            posIndex++;
            if (posIndex == 5) {
                if(Double.compare(scrollY, virtualizedScrollPane.getEstimatedScrollY()) == 0) {
                    if (e.getDeltaY() < 0) { // && virtualizedScrollPane.getEstimatedScrollY()/virtualizedScrollPane.getTotalHeightEstimate() > 0.8d
                        if (loadLastCallback != null) {
                            isLoading = true;
                            List<T> newDataList = loadLastCallback.call(LimitDataListView.this);
                            if (newDataList != null) {
                                addLasts(newDataList);
                            }
                            isLoading = false;
                        }
                    } else if (e.getDeltaY() > 0 && virtualizedScrollPane.getEstimatedScrollY()/virtualizedScrollPane.getTotalHeightEstimate() < 0.05d) {
                        if (loadFrontCallback != null) {
                            isLoading = true;
                            List<T> newDataList = loadFrontCallback.call(LimitDataListView.this);
                            if (newDataList != null) {
                                addFronts(newDataList);
                            }
                            isLoading = false;
                        }
                    }
                }
                posIndex = 0;
            }
        });
    }
}
